home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / smail-3.1.28 / src / header.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-07-11  |  26.7 KB  |  1,057 lines

  1. /* @(#)src/header.c    1.18 7/11/92 11:49:22 */
  2.  
  3. /*
  4.  *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  5.  *    Copyright (C) 1992  Ronald S. Karr
  6.  * 
  7.  * See the file COPYING, distributed with smail, for restriction
  8.  * and warranty information.
  9.  */
  10.  
  11. /*
  12.  * header.c:
  13.  *    routines to process message headers.
  14.  *
  15.  *    external functions: process_header, read_header, write_header
  16.  */
  17. #include <stdio.h>
  18. #include <ctype.h>
  19. #include <pwd.h>
  20. #include "defs.h"
  21. #include "smail.h"
  22. #include "field.h"
  23. #include "addr.h"
  24. #include "transport.h"
  25. #include "main.h"
  26. #include "spool.h"
  27. #include "log.h"
  28. #include "exitcodes.h"
  29. #include "dys.h"
  30. #ifndef DEPEND
  31. # include "extern.h"
  32. # include "debug.h"
  33. #endif
  34.  
  35. /* variables defined in this file */
  36. struct list *header;            /* list of header fields */
  37.  
  38. /* variables local to this file */
  39. static int use_resent;            /* TRUE if Resent- fields exist */
  40. static int remove_bcc_fields;        /* TRUE to remove Bcc: header fields */
  41. static char *to_fn;            /* name of To: field */
  42. static char *from_fn;            /* name of From: field */
  43. static char *sender_fn;            /* name of the Sender: field */
  44. static char *cc_fn;            /* name of the Cc: field */
  45. static char *bcc_fn;            /* name of the Bcc: field */
  46. static char *message_id_fn;        /* name of the Message-Id: field */
  47. static int found_to;            /* TRUE if To: field was found */
  48. static int found_message_id;        /* TRUE if Message-Id: field found */
  49. static int found_from;            /* TRUE if From: field was found */
  50. static int found_cc;            /* TRUE if Cc: field was found */
  51. static int found_bcc;            /* TRUE if Bcc: field was found */
  52. static int found_date;            /* TRUE if Date: field was found */
  53. static int found_sender;        /* TRUE if Sender: field was found */
  54. static int count_received;        /* count of Received: fields found */
  55. static struct addr *header_from;    /* list of From: addrs */
  56. static struct addr *header_sender;    /* list of Sender: addrs */
  57. static struct list *remote_header;    /* simple remote-form header */
  58. static struct list *strict_header;    /* strict RFC822 header */
  59.  
  60. /* functions local to this file */
  61. static void check_for_resent();
  62. static void scan_header();
  63. static void new_field();
  64. static char *build_to_field();
  65. static char *build_date_field();
  66. static char *build_message_id_field();
  67. static char *build_from_field();
  68. static char *build_received_field();
  69. static char *build_return_path();
  70. static void build_remote_header();
  71. static void build_strict_header();
  72.  
  73.  
  74. /*
  75.  * process_header - first pass processing of a header.
  76.  *
  77.  * Note the existence of certain headers and, if input rq is non-nil
  78.  * fill it with a list of recipient addresses taken from either the
  79.  * Resent-To: Resent-Cc: and Resent-Bcc: fields or from the To: Cc:
  80.  * and Bcc: fields.
  81.  */
  82. char *
  83. process_header(rq)
  84.     struct addr **rq;            /* put list of recipients here */
  85. {
  86.     char *error;
  87.  
  88.     /*
  89.      * initialize the data to be searched for in the header
  90.      */
  91.     use_resent = FALSE;
  92.     found_to = FALSE;            /* nothing found yet */
  93.     found_message_id = FALSE;
  94.     found_from = FALSE;
  95.     found_date = FALSE;
  96.     found_sender = FALSE;
  97.     found_cc = FALSE;
  98.     found_bcc = FALSE;
  99.     count_received = 0;
  100.     header_from = NULL;            /* initialize From: addr list */
  101.     header_sender = NULL;        /* initialize Sender: addr list */
  102.     strict_header = NULL;        /* no strict RFC822 header yet */
  103.  
  104.     /*
  105.      * Bcc: fields are removed only if we are processing the header
  106.      * for addresses, otherwise we assume that the user agent would
  107.      * have removed them if it didn't want them passed along
  108.      */
  109.     remove_bcc_fields = (rq != NULL);
  110.  
  111.     check_for_resent();            /* setup use_resent */
  112.     error = NULL;
  113.     scan_header(rq, &error);        /* scan for fields and recipients */
  114.  
  115.     if (rq && error) {
  116.     /* got an error message while extracting recipients, done */
  117.     return error;
  118.     }
  119.     error = NULL;
  120.  
  121.     /*
  122.      * if no recipient fields given, create a To: field containing
  123.      * the known recipients
  124.      */
  125.     if (! (found_to || found_cc || found_bcc) && rq == NULL) {
  126.     new_field(build_to_field());
  127.     }
  128.  
  129.     /*
  130.      * if no From: field exists, and no Sender: field exists, create
  131.      * a From: field.
  132.      *
  133.      * otherwise, if no Sender: field exists and From: is not verified
  134.      * to be the actual sender, create a Sender: field.
  135.      */
  136.     if (! (found_from || found_sender)) {
  137.     if (!islocal) {
  138.         from_fn = use_resent?
  139.             "Apparently-Resent-From":
  140.             "Apparently-From";
  141.     }
  142.     new_field(build_from_field(from_fn));
  143.     } else if (islocal && found_from) {
  144.     /*
  145.      * With no or multiple addresses in the From: field, always
  146.      * generate a Sender: field.
  147.      */
  148.     if (! header_from || header_from->succ) {
  149.         new_field(build_from_field(sender_fn));
  150.     } else if (!sender_is_trusted) {
  151.         /*
  152.          * if the sender is not trusted, allow the From: field only
  153.          * if it is in a form considered valid for the user.
  154.          */
  155.         char *work_addr = COPY_STRING(header_from->work_addr);
  156.         char *target;
  157.         char *remain;
  158.         int parseflags = header_from->parseflags;
  159.         int form = parse_address(work_addr, &target, &remain, &parseflags);
  160.         struct passwd *pw;
  161.         int dobuild = FALSE;
  162.  
  163.         switch (form) {
  164.         case FAIL:
  165.         case PARSE_ERROR:
  166.         dobuild = TRUE;
  167.         break;
  168.  
  169.         /*
  170.          * catch all of the remote cases here
  171.          */
  172.  
  173.         default:
  174.         if (! islocalhost(target) && !EQIC(target, visible_name)) {
  175.             dobuild = TRUE;
  176.             break;
  177.         }
  178.         form = parse_address(remain, (char *)NULL, &error, &parseflags);
  179.         if (form != LOCAL) {
  180.             dobuild = TRUE;
  181.             break;
  182.         }
  183.         /*FALLTHROUGH*/
  184.  
  185.         case LOCAL:
  186.         pw = getpwbyname(remain);
  187.         if (pw == NULL || pw->pw_uid != real_uid)
  188.             dobuild = TRUE;
  189.         break;
  190.         }
  191.         if (dobuild)
  192.         new_field(build_from_field(sender_fn));
  193.         xfree(work_addr);
  194.     }
  195.     }
  196.  
  197.     /*
  198.      * if no date field given, create one
  199.      */
  200.     if (!found_date) {
  201.     new_field(build_date_field());
  202.     }
  203.  
  204.     /*
  205.      * if no message-id field given, create one
  206.      */
  207.     if (!found_message_id) {
  208.     new_field(build_message_id_field());
  209.     }
  210.  
  211.     /*
  212.      * note count of received lines if count is not already known
  213.      */
  214.     if (hop_count < 0) {
  215.     hop_count = count_received;
  216.     }
  217.  
  218.     return error;            /* return any error messages */
  219. }
  220.  
  221. /*
  222.  * set use_resent if there exist any standard Resent- fields in
  223.  * the header
  224.  */
  225. static void
  226. check_for_resent()
  227. {
  228.     register struct list *q;        /* index to headers */
  229.  
  230.     /* on the first pass check for Resent- headers */
  231.     for (q = header; q; q = q->succ) {
  232.  
  233.     /*
  234.      * some systems (at least pcc for microport/286) can't handle
  235.      * an expression with four HDREQ macros in one if, so split it
  236.      * up into two if's.
  237.      */
  238.  
  239.     if (HDREQ("resent-to", q->text) ||
  240.         HDREQ("resent-from", q->text))
  241.     {
  242.         use_resent = TRUE;
  243.         break;
  244.     }
  245.     if (HDREQ("resent-cc", q->text) ||
  246.         HDREQ("resent-bcc", q->text))
  247.     {
  248.         use_resent = TRUE;
  249.         break;
  250.     }
  251.     }
  252.  
  253.     /* set the address field names based on this information */
  254.     if (use_resent) {
  255.     to_fn = "Resent-To";
  256.     from_fn = "Resent-From";
  257.     sender_fn = "Resent-Sender";
  258.     cc_fn = "Resent-Cc";
  259.     bcc_fn = "Resent-Bcc";
  260.     message_id_fn = "Resent-Message-Id";
  261.     } else {
  262.     to_fn = "To";
  263.     from_fn = "From";
  264.     sender_fn = "Sender";
  265.     cc_fn = "Cc";
  266.     bcc_fn = "Bcc";
  267.     message_id_fn = "Message-Id";
  268.     }
  269. }
  270.  
  271. /*
  272.  * scan_header -  pass through searching for required headers.
  273.  *
  274.  * If we are extracting recipient addresses from the header, do
  275.  * so here.
  276.  */
  277. static void
  278. scan_header(rq, error)
  279.     struct addr **rq;            /* put recipients here, if non-NULL */
  280.     char **error;            /* store error message here */
  281. {
  282.     register struct list *q;        /* temp for scanning header */
  283.  
  284.     for (q = header; q; q = q->succ) {
  285.     if (hop_count < 0 && HDREQ("Received", q->text)) {
  286.         count_received++;        /* count Received: fields */
  287.     } else if (HDREQ(to_fn, q->text)) {
  288.         found_to = TRUE;
  289.         if (islocal) {
  290.         q->text = process_field(q->text,
  291.                     index(q->text,':')+1,
  292.                     (char *)NULL,
  293.                     (char *)NULL,
  294.                     rq,
  295.                     islocal?F_LOCAL:0,
  296.                     error);
  297.         } else if (rq) {
  298.         (void) process_field((char *)NULL, index(q->text, ':') + 1,
  299.                      (char *)NULL, (char *)NULL, rq,
  300.                      0, error);
  301.         }
  302.     } else if (HDREQ("apparently-to", q->text) ||
  303.            HDREQ("to", q->text) ||
  304.            HDREQ("resent-to", q->text))
  305.     {
  306.         found_to = TRUE;
  307.     } else if (HDREQ(message_id_fn, q->text)) {
  308.         found_message_id = TRUE;
  309.     } else if (HDREQ(from_fn, q->text)) {
  310.         found_from = TRUE;
  311.         if (islocal) {
  312.         q->text = process_field(q->text,
  313.                     index(q->text, ':')+1,
  314.                     (char *)NULL,
  315.                     (char *)NULL,
  316.                     &header_from,
  317.                     islocal?F_LOCAL:0,
  318.                     error);
  319.         } else {
  320.         (void) process_field((char *)NULL, index(q->text, ':') + 1,
  321.                      (char *)NULL, (char *)NULL,
  322.                      &header_from, 0, error);
  323.         }
  324.     } else if (HDREQ("date", q->text)) {
  325.         found_date = TRUE;
  326.     } else if (HDREQ(sender_fn, q->text)) {
  327.         if (!sender_is_trusted) {
  328.         struct list **pq;
  329.         /*
  330.          * Non-trusted users can't supply their own Sender: fields,
  331.          * so delete this field.
  332.          */
  333.         for (pq = &header; *pq && *pq != q; pq = &(*pq)->succ) ;
  334.         if (*pq) {
  335.             *pq = q->succ;
  336.         }
  337.         } else {
  338.         found_sender = TRUE;
  339.         if (islocal) {
  340.             q->text = process_field(q->text,
  341.                         index(q->text, ':')+1,
  342.                         (char *)NULL,
  343.                         (char *)NULL,
  344.                         &header_from,
  345.                         islocal?F_LOCAL:0,
  346.                         error);
  347.         } else {
  348.             (void) process_field((char *)NULL, index(q->text, ':') + 1,
  349.                      (char *)NULL, (char *)NULL,
  350.                      &header_from, 0, error);
  351.         }
  352.         }
  353.     } else if (HDREQ(cc_fn, q->text)) {
  354.         found_cc = TRUE;
  355.         if (islocal) {
  356.         q->text = process_field(q->text,
  357.                     index(q->text, ':') + 1,
  358.                     (char *)NULL,
  359.                     (char *)NULL,
  360.                     rq,
  361.                     islocal?F_LOCAL:0,
  362.                     error);
  363.         } else if (rq) {
  364.         (void) process_field((char *)NULL, index(q->text, ':') + 1,
  365.                      (char *)NULL, (char *)NULL, rq,
  366.                      0, error);
  367.         }
  368.     } else if (HDREQ(bcc_fn, q->text)) {
  369.         found_bcc = TRUE;
  370.         if (islocal) {
  371.         q->text = process_field(q->text,
  372.                     index(q->text, ':') + 1,
  373.                     (char *)NULL,
  374.                     (char *)NULL,
  375.                     rq,
  376.                     islocal?F_LOCAL:0,
  377.                     error);
  378.         } else if (rq) {
  379.         (void) process_field((char *)NULL, index(q->text, ':') + 1,
  380.                      (char *)NULL, (char *)NULL, rq,
  381.                      0, error);
  382.         }
  383.     } else if (HDREQ("precedence", q->text)) {
  384.         /*
  385.          * a precedence field is specified, use it to set the message
  386.          * priority
  387.          */
  388.         int new_grade = parse_precedence(index(q->text, ':'));
  389.  
  390.         if (new_grade) {
  391.         msg_grade = new_grade;
  392.         }
  393.     }
  394.     }
  395. }
  396.  
  397.  
  398. /*
  399.  * Create a To: header field
  400.  */
  401. static char *
  402. build_to_field()
  403. {
  404.     register struct addr *rq;        /* recipients to put in To: field */
  405.     struct str str;            /* region for string macros */
  406.     int col;                /* current output line column */
  407.  
  408.     rq = recipients;
  409.  
  410.     /*
  411.      * Sendmail inserts Apparently-To: fields because RFC822 people
  412.      * complained about it being against the rules to add a To:
  413.      * line.  We cheat.  If the letter is not originated locally,
  414.      * use the Apparently-To: convention.  Otherwise do it the
  415.      * right way.
  416.      *
  417.      * We'll see if anybody complains
  418.      */
  419.     if (!islocal) {
  420.     if (use_resent) {
  421.         to_fn = "Apparently-Resent-To";
  422.     } else {
  423.         to_fn = "Apparently-To";
  424.     }
  425.     }
  426.  
  427.     /*
  428.      * now we actually form the To: line from the known recipient
  429.      * addresses
  430.      */
  431.     STR_INIT(&str);            /* initialize dynamic string region */
  432.  
  433.     /* form = To: recipient(, recipient)* */
  434.  
  435.     rq = recipients;
  436.     /* special case, insert the first address */
  437.     if (rq) {
  438.     if (rq->in_addr[0] == '@') {
  439.         /* route-addr, put it in angle brackets */
  440.         str_printf(&str, "%s: <%s>", to_fn, rq->in_addr);
  441.     } else {
  442.         /* otherwise just use the address */
  443.         str_printf(&str, "%s: %s", to_fn, rq->in_addr);
  444.     }
  445.     rq = rq->succ;
  446.     }
  447.     col = str.i;
  448.     while (rq) {
  449.     int len = strlen(rq->in_addr);
  450.  
  451.     STR_NEXT(&str, ',');
  452.     col++;
  453.  
  454.     /*
  455.      * put at least 27-chars on a line, try to keep down below 72
  456.      * NOTE:  we aren't counting TAB characters hidden in the
  457.      *      addresses.
  458.      */
  459.     if (col > 27 && col + len > 72) {
  460.         STR_NEXT(&str, '\n');
  461.         STR_NEXT(&str, '\t');
  462.         col = 8;
  463.     } else {
  464.         STR_NEXT(&str, ' ');
  465.         col++;
  466.     }
  467.  
  468.     if (rq->in_addr[0] == '@') {
  469.         str_printf(&str, "<%s>", rq->in_addr);
  470.         len += 2;            /* 2 chars for the angel brackets */
  471.     } else {
  472.         STR_CAT(&str, rq->in_addr);
  473.     }
  474.     col += len;
  475.     rq = rq->succ;
  476.     }
  477.     STR_NEXT(&str, '\n');
  478.     STR_NEXT(&str, '\0');
  479.  
  480.     STR_DONE(&str);
  481.     return str.p;            /* return finished To: field */
  482. }
  483.  
  484. /*
  485.  * build a Date: field
  486.  */
  487. static char *
  488. build_date_field()
  489. {
  490.     register char *s;            /* returned header field */
  491.  
  492.     if (date_field &&
  493.     (s = expand_string(date_field, (struct addr *)NULL,
  494.                (char *)NULL, (char *)NULL)))
  495.     {
  496.     return COPY_STRING(s);
  497.     }
  498.     return "";
  499. }
  500.  
  501. /*
  502.  * Form a Message-Id: header field.
  503.  */
  504. static char *
  505. build_message_id_field()
  506. {
  507.     register char *s;            /* returned header field */
  508.  
  509.     if (message_id_field &&
  510.     (s = expand_string(message_id_field, (struct addr *)NULL,
  511.                (char *)NULL, (char *)NULL)))
  512.     {
  513.     if (use_resent) {
  514.         return xprintf("Resent-%s", s);
  515.     } else {
  516.         return COPY_STRING(s);
  517.     }
  518.     }
  519.     return "";
  520. }
  521.  
  522. /*
  523.  * form a From: or Sender: header field
  524.  */
  525. static char *
  526. build_from_field(fn)
  527.     char *fn;                /* field name */
  528. {
  529.     char *s;
  530.  
  531.     if (from_field)
  532.     s = expand_string(from_field, (struct addr *)0, (char *)0, sender);
  533.     if (s == NULL || *s == '\0')
  534.     return "";
  535.     if (! HDREQ("from", s))
  536.     return COPY_STRING(s);
  537.     return xprintf("%s%s", fn, s + 4);
  538. }
  539.  
  540. /*
  541.  * link in a brand new field with the given text
  542.  */
  543. static void
  544. new_field(field)
  545.     char *field;            /* new field to add */
  546. {
  547.     register struct list *f;        /* header field entry */
  548.  
  549.     f = (struct list *)xmalloc(sizeof(struct list)); /* create it */
  550.     f->text = field;
  551.     f->succ = header;            /* link it in */
  552.     header = f;
  553. }
  554.  
  555. /*
  556.  * parse_precedence - parse a Precedence: field and return it's grade value
  557.  *
  558.  * use tokenize to parse for an atom (the first atom is used, the rest are
  559.  * ignored).  Then lookup the atom in the grades string and return the
  560.  * associated grade, if found, 0 otherwise.
  561.  */
  562. int
  563. parse_precedence(field)
  564.     char *field;
  565. {
  566.     int len;                /* length of token */
  567.     struct token *tok;            /* list of tokens from tokenize */
  568.     register char *p = grades;        /* temp for scanning grades */
  569.     char *error;            /* error message from tokenize */
  570.  
  571.     DEBUG(DBG_HEADER_HI, "parse_precedence called\n");
  572.     error = tokenize(field, &tok, FALSE, TRUE);
  573.     if (error) {
  574.     /* tokenize didn't like the field for some reason */
  575.     write_log(LOG_MLOG, "Error in precedence field: %s", error);
  576.     return '\0';            /* no precedence value */
  577.     }
  578.     if (tok->form != T_TEXT) {
  579.     return '\0';            /* not a text (atom) token */
  580.     }
  581.     len = strlen(tok->text);
  582.     DEBUG1(DBG_HEADER_MID, "precedence is %s\n", tok->text);
  583.     while (p) {
  584.     if (strncmpic(tok->text, p, len) == 0 && p[len] == ':') {
  585.         /* we have a match, grab the character */
  586.         return p[len+1];
  587.     }
  588.     /* skip to end of precedence string */
  589.     p = index(p, ':');
  590.     if (p) {
  591.         /* skip past grade character */
  592.         p = index(p, ':');
  593.         if (p) {
  594.         p++;            /* now at next precedence string */
  595.         }
  596.     }
  597.     }
  598.  
  599.     DEBUG(DBG_QUEUE_MID, "unknown precedence\n");
  600.     return '\0';            /* unknown precedence */
  601. }
  602.  
  603.  
  604. /*
  605.  * read_header - initialize header structures, reading from spool file
  606.  *
  607.  * Read a header on the spool file.  The header data structure is
  608.  * initialized to contain an entry for each field of the header.
  609.  *
  610.  * returns SUCCEED or FAIL, the actual failure is logged.
  611.  */
  612. int
  613. read_header()
  614. {
  615.     struct str field;            /* input field */
  616.     register int c;            /* current input char */
  617.     int last_c = EOF;            /* last input char */
  618.     struct list **link_addr;        /* forward link for next field */
  619.     long line_mark;            /* marks beginning of a line */
  620.  
  621.     link_addr = &header;
  622.  
  623.     /*
  624.      * loop until done reading the header
  625.      */
  626.     for (;;) {
  627.     STR_INIT(&field);
  628.     /*
  629.      * field name is some non-space chars followed by
  630.      * optional white space up to a ':'
  631.      */
  632.     if (last_c == ':') {
  633.         /* we had a read ahead character past the last newline */
  634.         c = EOF;            /* don't allow null fieldnames */
  635.     } else {
  636.         if (last_c != EOF) {
  637.         STR_NEXT(&field, last_c);
  638.         line_mark = tell_spool() - 1;
  639.         last_c = EOF;
  640.         } else {
  641.         /*
  642.          * save the beginning of the line, so we can go back to
  643.          * here if this is not a header
  644.          */
  645.         line_mark = tell_spool();
  646.         }
  647.         while ((c = GETSPOOL()) != EOF && c != READ_FAIL &&
  648.            !isspace(c) && c != ':')
  649.         {
  650.         STR_NEXT(&field, c);
  651.         last_c = c;
  652.         }
  653.         while (c != EOF && c != READ_FAIL &&
  654.            isspace(c))
  655.         {
  656.             STR_NEXT(&field, c);
  657.             c = GETSPOOL();
  658.         }
  659.         if (c == READ_FAIL) {
  660.         write_log(LOG_SYS, "read error in spool file <%s/%s>",
  661.               spool_dir, spool_fn);
  662.         return FAIL;
  663.         }
  664.     }
  665.  
  666.     /* not a valid field name, must be past the header */
  667.     if (c != ':') {
  668.         break;
  669.     }
  670.  
  671.     /*
  672.      * looks like we are in a header field, so scan to the end of it
  673.      */
  674.     STR_NEXT(&field, c);
  675.     last_c = c;
  676.  
  677.     /*
  678.      * header field terminated by new line which does not start
  679.      * with a SPACE or TAB char, or by EOF.
  680.      */
  681.     while ((c = GETSPOOL()) != EOF && c != READ_FAIL &&
  682.            !(last_c == '\n' && c != ' ' && c != '\t'))
  683.     {
  684.         STR_NEXT(&field, c);
  685.         last_c = c;
  686.     }
  687.     if (c == READ_FAIL) {
  688.         write_log(LOG_SYS, "read error in spool file <%s/%s>",
  689.               spool_dir, spool_fn);
  690.     }
  691.  
  692.     last_c = c;            /* preserve this for the next field */
  693.     STR_NEXT(&field, '\0');
  694.     STR_DONE(&field);
  695.     *link_addr = (struct list *)xmalloc(sizeof(struct list));
  696.     (*link_addr)->text = field.p;
  697.     link_addr = &(*link_addr)->succ;
  698.     if (c == EOF || c == '\n') {
  699.         line_mark = -1;        /* don't seek back to previous line */
  700.         field.p = NULL;
  701.         field.i = 0;
  702.         break;
  703.     }
  704.     }
  705.  
  706.     *link_addr = NULL;            /* terminate header queue */
  707.  
  708.     /*
  709.      * all done reading the field, cleanup, seek to beginning of
  710.      * message body (after last valid field line)
  711.      */
  712.     if (line_mark >= 0 && seek_spool(line_mark) == FAIL) {
  713.     write_log(LOG_SYS, "seek error on spool file <%s/%s>",
  714.           spool_dir, spool_fn);
  715.     return FAIL;
  716.     }
  717.     if (field.p) {
  718.     STR_DONE(&field);        /* free the field that wasn't */
  719.     STR_FREE(&field);
  720.     }
  721.     return SUCCEED;
  722. }
  723.  
  724. /*
  725.  * write_header - write out the header to a stdio FILE
  726.  *
  727.  * inputs:
  728.  *    file    - file to write to.
  729.  *    addr    - involved address, first in linked list is used
  730.  *
  731.  * outputs:
  732.  *    SUCCEED or FAIL
  733.  */
  734. int
  735. write_header(f, addr)
  736.     FILE *f;                /* file to send header to */
  737.     struct addr *addr;            /* transport */
  738. {
  739.     long flags = addr->transport->flags; /* transport flags */
  740.     struct list *q, *q2;        /* temp for processing header */
  741.     int crlf = (flags & PUT_CRLF)?1:0;    /* put \r\n at end of each line? */
  742.     char *s;
  743.  
  744.     /* output transport-specific insertion headers */
  745.     for (q2 = addr->transport->hdrinsert; q2; q2 = q2->succ) {
  746.     if (write_field(expand_string(q2->text, addr,
  747.             (char *)NULL, (char *)NULL), f, crlf) == FAIL)
  748.         return FAIL;
  749.     }
  750.  
  751.     /* output a Return-Path: header if required */
  752.     if (flags & PUT_RETURNPATH) {
  753.     if (write_field(build_return_path(addr), f, crlf) == FAIL) {
  754.         return FAIL;
  755.     }
  756.     }
  757.  
  758.     /* output a Received: header if required */
  759.     if (flags & PUT_RECEIVED) {
  760.     if (write_field(build_received_field(addr), f, crlf) == FAIL) {
  761.         return FAIL;
  762.     }
  763.     }
  764.  
  765.     if (use_resent) {
  766.     /*
  767.      * the situation gets complex if resent- headers are involved,
  768.      * so ignore the issue by not changing anything.
  769.      */
  770.     q = header;
  771.     } else if (flags & STRICT_TPORT) {
  772.     /* output a strict header, building one if none exists */
  773.     if (strict_header == NULL) {
  774.         build_strict_header();
  775.     }
  776.     q = strict_header;
  777.     } else if (flags & LOCAL_XFORM) {
  778.     /* output header as is */
  779.     q = header;
  780.     } else {
  781.     /* output a normal remote header */
  782.     if (remote_header == NULL) {
  783.         build_remote_header();
  784.     }
  785.     q = remote_header;
  786.     }
  787.  
  788.     /*
  789.      * output the appropriate header, as selected above
  790.      */
  791.     for (; q; q = q->succ) {
  792.     /*
  793.      * remote bcc fields if addresses were extracted from the
  794.      * header.  Otherwise, we expect that the UA would have
  795.      * removed them if they were not wanted.
  796.      *
  797.      * TODO: should we always remove bcc: fields from locally
  798.      *     generated mail?
  799.      */
  800.     if (remove_bcc_fields && HDREQ(bcc_fn, q->text)) {
  801.         continue;
  802.     }
  803.     /* don't output redundant Return-Path headers */
  804.     if ((flags & PUT_RETURNPATH) && HDREQ("Return-Path", q->text)) {
  805.         continue;
  806.     }
  807.     /* output transport-specific insertion headers */
  808.     for (q2 = addr->transport->hdrremove; q2; q2 = q2->succ) {
  809.         s = expand_string(q2->text, addr, (char *)NULL, (char *)NULL);
  810.         if (s && HDREQ(s, q->text))
  811.         break;
  812.     }
  813.     if (q2 == NULL && write_field(q->text, f, crlf) == FAIL) {
  814.         return FAIL;
  815.     }
  816.     }
  817.  
  818.     /* output transport-specific insertion headers */
  819.     for (q2 = addr->transport->hdrappend; q2; q2 = q2->succ) {
  820.     if (write_field(expand_string(q2->text, addr,
  821.             (char *)NULL, (char *)NULL), f, crlf) == FAIL)
  822.         return FAIL;
  823.     }
  824.  
  825.     DEBUG(DBG_HEADER_LO, "\n");
  826.  
  827.     return SUCCEED;
  828. }
  829.  
  830. /*
  831.  * write_field - output a header field, possibly changing \n to \r\n.
  832.  *
  833.  * return SUCCEED or FAIL.
  834.  */
  835. int
  836. write_field(field, f, crlf)
  837.     register char *field;        /* field to be written */
  838.     FILE *f;                /* file to send to */
  839.     int crlf;                /* TRUE to write \r\n for \n */
  840. {
  841.     register int c;
  842.  
  843.     if (field == NULL || field[0] == '\0') {
  844.     /* empty field, don't write out anything */
  845.     return SUCCEED;
  846.     }
  847.     DEBUG1(DBG_HEADER_LO, "%s", field);
  848.     while (c = *field++) {
  849.     /* write out c, possibly with a '\r' first */
  850.     if ((c == '\n' && crlf && putc('\r', f) == EOF) || putc(c, f) == EOF) {
  851.         return FAIL;
  852.     }
  853.     }
  854.     /* if field did not end with \n, supply it */
  855.     if (field[-2] != '\n') {
  856.     DEBUG(DBG_HEADER_LO, "\n");
  857.     if (crlf) {
  858.         if (putc('\r', f) == EOF) {
  859.         return FAIL;
  860.         }
  861.     }
  862.     if (putc('\n', f) == EOF) {
  863.         return FAIL;
  864.     }
  865.     }
  866.     return SUCCEED;
  867. }
  868.  
  869. /*
  870.  * form a Received: header field
  871.  */
  872. static char *
  873. build_received_field(addr)
  874.     struct addr *addr;
  875. {
  876.     register char *s;            /* returned header field */
  877.  
  878.     if (received_field &&
  879.     (s = expand_string(received_field, addr, (char *)NULL, (char *)NULL)))
  880.     {
  881.     return s;
  882.     }
  883.     return "";
  884. }
  885.  
  886. /*
  887.  * build a Return-Path: header field.
  888.  */
  889. static char *
  890. build_return_path(addr)
  891.     struct addr *addr;
  892. {
  893.     register char *s;            /* returned header field */
  894.  
  895.     if (return_path_field &&
  896.     (s = expand_string(return_path_field, addr,
  897.                (char *)NULL, (char *)NULL)))
  898.     {
  899.     return s;
  900.     }
  901.     return "";
  902. }
  903.  
  904.  
  905. /*
  906.  * build_remote_header - build a simple remote-form header
  907.  *
  908.  * For From: and Sender: fields prepend localhost! on addresses which
  909.  * are pure !-routes, if mail is originated local then convert user to
  910.  * user@localdomain and qualify known domain abbreviations.
  911.  */
  912. static void
  913. build_remote_header()
  914. {
  915.     struct list **rhq = &remote_header; /* where to put next field */
  916.     struct list *hq;            /* temp for scanning header */
  917.     char *error;            /* (ignored) error message */
  918.  
  919.     /*
  920.      * copy the standard header to the remote header, transforming
  921.      * sender and recipient fields as appropriate.
  922.      */
  923.     for (hq = header; hq; hq = hq->succ) {
  924.     *rhq = (struct list *)xmalloc(sizeof(**rhq));
  925.     (*rhq)->succ = NULL;
  926.     if (HDREQ(from_fn, hq->text) || HDREQ(sender_fn, hq->text)) {
  927.         /*
  928.          * field containing a sender address:
  929.          *    prepend uucp_host! in !-routes
  930.          *    append @visiblename after local-form addresses
  931.          *    qualify known domain abbreviations, if local
  932.          */
  933.         (*rhq)->text = process_field(hq->text,
  934.                      index(hq->text, ':') + 1,
  935.                      visible_name,
  936.                      uucp_name,
  937.                      (struct addr **)NULL,
  938.                      (islocal? F_LOCAL: 0),
  939.                      &error);
  940.     } else if (islocal && (HDREQ(to_fn, hq->text) ||
  941.                    HDREQ(cc_fn, hq->text) ||
  942.                    HDREQ(bcc_fn, hq->text)))
  943.     {
  944.         /*
  945.          * locally generated field containing recipient addresses:
  946.          *    append @visible_name after local-form addresses
  947.          *    qualify known domain abbreviations
  948.          */
  949.         (*rhq)->text = process_field(hq->text,
  950.                      index(hq->text, ':') + 1,
  951.                      visible_name,
  952.                      (char *)NULL,
  953.                      (struct addr **)NULL,
  954.                      F_LOCAL,
  955.                      &error);
  956.     } else {
  957.         (*rhq)->text = hq->text;
  958.     }
  959.  
  960.     rhq = &(*rhq)->succ;
  961.     }
  962. }
  963.  
  964. /*
  965.  * build_strict_header - build a somewhat RFC822 conformant header
  966.  *
  967.  * For addresses which are in local form, in !-route form or in
  968.  * remainder%target form, append @visiblename and prepend a !-route
  969.  * path back to the sender.  This essentially qualifies the address
  970.  * within the context of the current host, which will then be
  971.  * routed through on replies to handle the !-routes.
  972.  */
  973. static void
  974. build_strict_header()
  975. {
  976.     char *error;            /* error messages */
  977.     struct list **shq = &strict_header; /* where to put next field */
  978.     struct list *hq;            /* temp for scanning header */
  979.     char *sender_path;            /* path to sender's machine */
  980.  
  981.     /*
  982.      * build a path back to the sender's machine, which is a !-route
  983.      * back to the sender with the last component (!username) removed
  984.      * this will be passed to process_field as the uucp_name parameter
  985.      * for recipient fields.
  986.      */
  987.     sender_path = build_uucp_route(sender, &error, 0);
  988.     if (sender_path == NULL) {
  989.     /*
  990.      * errors will generate a NULL sender path, so nothing will
  991.      * be prepended to addresses in recipient fields
  992.      */
  993.     write_log(LOG_MLOG, "error building path to sender <%s>: %s",
  994.           sender, error);
  995.     } else {
  996.     char *p = sender_path + strlen(sender_path);
  997.  
  998.     /*
  999.      * back up over the trailing "!token" and terminate the string
  1000.      */
  1001.     p = back_address_token(sender_path, p);
  1002.     if (p == NULL) {
  1003.         write_log(LOG_MLOG,
  1004.          "error building path to sender <%s>: unexpected end of token",
  1005.               sender);
  1006.         sender_path = NULL;
  1007.     } else if (p == sender_path) {
  1008.         sender_path = NULL;
  1009.     } else {
  1010.         p[-1] = '\0';        /* terminate the path */
  1011.     }
  1012.     }
  1013.  
  1014.     /*
  1015.      * copy the standard header to a new strict header, performing
  1016.      * the necessary transformations on sender and recipient fields
  1017.      */
  1018.     for (hq = header; hq; hq = hq->succ) {
  1019.     *shq = (struct list *)xmalloc(sizeof(**shq));
  1020.     (*shq)->succ = NULL;
  1021.     if (HDREQ(from_fn, hq->text) || HDREQ(sender_fn, hq->text)) {
  1022.         /*
  1023.          * field containing a sender address:
  1024.          *    append @visiblename after local-form addresses
  1025.          *    qualify known domain abbreviations, if local
  1026.          */
  1027.         (*shq)->text = process_field(hq->text,
  1028.                      index(hq->text, ':') + 1,
  1029.                      visible_name,
  1030.                      (char *)NULL,
  1031.                      (struct addr **)NULL,
  1032.                      (islocal? F_LOCAL: 0)|F_STRICT,
  1033.                      &error);
  1034.     } else if (HDREQ(to_fn, hq->text) || HDREQ(cc_fn, hq->text) ||
  1035.            HDREQ(bcc_fn, hq->text))
  1036.     {
  1037.         /*
  1038.          * field containing a recipient address:
  1039.          *    append @visiblename after !-route, local-form or u%d form
  1040.          *    prepend sender_path! before the same set of addresses
  1041.          *    qualify known domain abbreviations, if local
  1042.          */
  1043.         (*shq)->text = process_field(hq->text,
  1044.                      index(hq->text, ':') + 1,
  1045.                      visible_name,
  1046.                      sender_path,
  1047.                      (struct addr **)NULL,
  1048.                      (islocal? F_LOCAL: 0)|F_STRICT,
  1049.                      &error);
  1050.     } else {
  1051.         (*shq)->text = hq->text;
  1052.     }
  1053.  
  1054.     shq = &(*shq)->succ;
  1055.     }
  1056. }
  1057.